package cn.line5.seam.db;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;

import cn.line5.seam.exception.DBException;
import cn.line5.seam.exception.SeamException;

/**
 * <h1>查询抽象类</h1>
 * 
 * @author Liu,Xiaojun
 * @since JDK1.6
 */
public abstract class AbstractQuery implements Query {

	protected Connection conn;

	protected PreparedStatement ps;

	protected String sql;

	protected List<Parameter> parameterList = new ArrayList<Parameter>();

	protected Object obj;

	// sql和参数锁，锁定后不能修改
	protected Boolean sqlLock = false;

	/**
	 * 构造方法，只能由其继承者调用。
	 * 
	 * @param queryString
	 *            SQL语句
	 */
	public AbstractQuery(String queryString) {
		this.sql = queryString.trim();
	}

	/**
	 * 构造方法，只能由其继承者调用。
	 * 
	 * @param queryString
	 *            SQL语句
	 * @param obj
	 *            条件对象，将根据SQL对应的:xxx调用条件对象对应的属性值
	 */
	public AbstractQuery(String queryString, Object obj) {
		this.sql = queryString.trim();
		this.obj = obj;
	}

	/**
	 * 设置参数，final方法，不允许被覆盖。
	 * 
	 * @param position
	 *            参数标识
	 * @param val
	 *            参数
	 * @param type
	 *            参数类型，如果参数类型为空将根据传入参数自动判断其类型
	 */
	public final Query setParameter(Integer position, Object val,
			ParameterType type) {
		if (!this.sqlLock)
			if (type == null)
				this.parameterList.add(new Parameter(position, val));
			else
				this.parameterList.add(new Parameter(position, val, type));
		return this;
	}

	/**
	 * 设置参数，final方法，不允许被覆盖。
	 * 
	 * @param position
	 *            参数标识
	 * @param val
	 *            参数
	 */
	public final Query setParameter(Integer position, Object val) {
		return setParameter(position, val, null);
	}

	/**
	 * 执行SQL
	 * 
	 * @return 执行成功的条数
	 * @throws DBException
	 *             SQL执行异常或不是被执行器执行
	 */
	public int execute() throws DBException {
		throw new DBException("Can't execute in Query.");
	}

	/**
	 * 执行查询单条数据
	 * 
	 * @return 装载数据的Map对象
	 * @throws DBException
	 *             SQL查询异常或不是被查询器执行
	 */
	public Map<String, Object> single() throws DBException {
		throw new DBException("Can't query in Query.");
	}

	/**
	 * 执行查询单条数据
	 * 
	 * @param <T>
	 *            装载数据类泛型
	 * @param clazz
	 *            数据类（VO）的class
	 * @return 装载传入数据类（VO）的数据
	 * @throws DBException
	 *             SQL查询异常或不是被查询器执行
	 */
	public <T> T single(Class<T> clazz) throws DBException {
		throw new DBException("Can't query in Query.");
	}

	/**
	 * 执行查询
	 * 
	 * @return 装载Map的数据列表
	 * @throws DBException
	 *             SQL查询异常或不是被查询器执行
	 */
	public List<Map<String, Object>> list() throws DBException {
		throw new DBException("Can't query in Query.");
	}

	/**
	 * 执行查询
	 * 
	 * @param <T>
	 *            装载数据类泛型
	 * @param clazz
	 *            数据类（VO）的class
	 * @return 装载传入数据类（VO）的数据列表
	 * @throws DBException
	 *             SQL查询异常或不是被查询器执行
	 */
	public <T> List<T> list(Class<T> clazz) throws DBException {
		throw new DBException("Can't query in Query.");
	}

	/**
	 * 查询记录数，执行含有count()的SQL
	 * 
	 * @return 记录数
	 * @throws DBException
	 *             SQL查询异常
	 */
	public Integer count() throws DBException {
		throw new DBException("Can't query in Query.");
	}

	/**
	 * 执行分页查询
	 * 
	 * @param <T>
	 *            装载数据类泛型
	 * @param clazz
	 *            数据类（VO）的class
	 * @return 装载传入数据类（VO）的分页数据对象
	 * @throws DBException
	 *             SQL查询异常
	 */
	public <T> PageResult<T> pageList(Class<T> clazz) throws DBException {
		throw new DBException("Can't query in Query.");
	}

	/**
	 * 执行分页查询
	 * 
	 * @return 装载数据的Map对象列表
	 * @throws DBException
	 *             SQL查询异常
	 */
	public PageResult<Map<String, Object>> pageList() throws DBException {
		throw new DBException("Can't query in Query.");
	}

	/**
	 * 设置每页行数
	 * 
	 * @param pageRows
	 *            每页行数
	 */
	public Query setPageRows(Integer pageRows) {
		throw new DBException("Can't set pageRows in Query.");
	}

	/**
	 * 设置当前页
	 * 
	 * @param currentPage
	 *            当前页
	 */
	public Query setCurrentPage(Integer currentPage) {
		throw new DBException("Can't set currentPage in Query.");
	}

	/**
	 * 关闭资源
	 */
	protected void releaseResource() throws DBException {
		try {
			if (ps != null)
				ps.close();
		} catch (SQLException e) {
			throw new DBException("Release the DB Resource is Error.", e);
		}
	}

	public final Query setString(int position, String val) {
		return setParameter(position, val, ParameterType.String);
	}

	public final Query setBoolean(int position, Boolean val) {
		return setParameter(position, val, ParameterType.Boolean);
	}

	public final Query setByte(int position, Byte val) {
		return setParameter(position, val, ParameterType.Byte);
	}

	public final Query setShort(int position, Short val) {
		return setParameter(position, val, ParameterType.Short);
	}

	public final Query setInteger(int position, Integer val) {
		return setParameter(position, val, ParameterType.Integer);
	}

	public final Query setLong(int position, Long val) {
		return setParameter(position, val, ParameterType.Long);
	}

	public final Query setFloat(int position, Float val) {
		return setParameter(position, val, ParameterType.Float);
	}

	public final Query setDouble(int position, Double val) {
		return setParameter(position, val, ParameterType.Double);
	}

	public final Query setBigDecimal(int position, BigDecimal val) {
		return setParameter(position, val, ParameterType.BigDecimal);
	}

	public final Query setDate(int position, Date val) {
		return setParameter(position, val, ParameterType.Date);
	}

	public final Query setTime(int position, Date val) {
		return setParameter(position, val, ParameterType.Time);
	}

	public final Query setTimestamp(int position, Date val) {
		return setParameter(position, val, ParameterType.Timestamp);
	}

	protected final void formatSql() {
		if (!this.sqlLock) {
			String[] str = (sql + " $LSEAM").split("\\?");
			StringBuffer sb = new StringBuffer();
			if (str.length - 1 != parameterList.size()) {
				throw new DBException(
						"Does not match the number of parameters and the SQL.");
			}
			Collections.sort(parameterList, new ParameterComparator());
			for (int i = 0; i < str.length; i++) {
				if (str[i].contains(":")) {
					String[] str2 = str[i].split("\\:");
					sb.append(str2[0]).append("?");
					for (int j = 1; j < str2.length; j++) {
						boolean t = false;
						String fieldName = "";
						for (char c : str2[j].toCharArray()) {
							if (c == ' ' || c == ',' || c == ')')
								t = true;
							if (t)
								sb.append(c);
							else
								fieldName += c;
						}
						sb.append((j == str2.length - 1) ? "" : "?");
						try {
							Field f = obj.getClass()
									.getDeclaredField(fieldName);
							f.setAccessible(true);
							Object o = f.get(obj);
							parameterList.add(i + j - 1, new Parameter(-1, o));
						} catch (IllegalArgumentException e) {
							throw new SeamException("The field '" + fieldName
									+ "' read error in the class "
									+ obj.getClass().getName() + ".", e);
						} catch (SecurityException e) {
							throw new SeamException("The field '" + fieldName
									+ "' read error in the class "
									+ obj.getClass().getName() + ".", e);
						} catch (IllegalAccessException e) {
							throw new SeamException("The field '" + fieldName
									+ "' read error in the class "
									+ obj.getClass().getName() + ".", e);
						} catch (NoSuchFieldException e) {
							throw new SeamException("The field '" + fieldName
									+ "' read error in the class "
									+ obj.getClass().getName() + ".", e);
						}
					}
					sb.append((i == str.length - 1) ? "" : "?");
				} else {
					sb.append(str[i]).append((i == str.length - 1) ? "" : "?");
				}
			}
			sql = sb.toString().replace(" $LSEAM", "").replace("$LSEAM", "");
			this.sqlLock = true;
		}
	}

	protected final void setParameter() throws SQLException {
		if (parameterList != null && parameterList.size() > 0)
			for (int i = 0; i < parameterList.size(); i++) {
				Parameter p = parameterList.get(i);
				switch (p.getType()) {
				case String:
					ps.setString(i + 1, (String) p.getObject());
					break;
				case Boolean:
					ps.setBoolean(i + 1, (Boolean) p.getObject());
					break;
				case Byte:
					ps.setByte(i + 1, (Byte) p.getObject());
					break;
				case Short:
					ps.setShort(i + 1, (Short) p.getObject());
					break;
				case Integer:
					ps.setInt(i + 1, (Integer) p.getObject());
					break;
				case Long:
					ps.setLong(i + 1, (Long) p.getObject());
					break;
				case Float:
					ps.setFloat(i + 1, (Float) p.getObject());
					break;
				case Double:
					ps.setDouble(i + 1, (Double) p.getObject());
					break;
				case BigDecimal:
					ps.setBigDecimal(i + 1, (BigDecimal) p.getObject());
					break;
				case Date:
					ps.setDate(i + 1,
							new java.sql.Date(((Date) p.getObject()).getTime()));
					break;
				case Time:
					ps.setTime(i + 1,
							new java.sql.Time(((Date) p.getObject()).getTime()));
					break;
				case Timestamp:
					ps.setTimestamp(
							i + 1,
							new java.sql.Timestamp(((Date) p.getObject())
									.getTime()));
					break;
				default:
					ps.setString(i + 1, p.getObject().toString());
					break;
				}
			}
	}
}

/**
 * 实现排序功能
 * 
 * @author liuxj
 * 
 */
class ParameterComparator implements Comparator<Parameter> {
	public int compare(Parameter p1, Parameter p2) {
		return p1.toString().compareTo(p2.toString());
	}
}